#ifndef __POLYTASK_H__
#define __POLYTASK_H__

#include "tbb/combinable.h"
#include "cnc/cnc.h"
#include "cnc/default_tuner.h"

using namespace std;

#include "cnc/debug.h"

// forward declaration
class polyTask_context;

#include "TaskTag.hpp"
#include "Threading.hpp"

// PolyTask core class
template <int TASK_ID, int TASKTAGDIM, int INTERTASKDIM> class polyTask_step {
public:
  mutable tbb::combinable<bool> pinned; // make private someday
  mutable tbb::combinable<double> overhead; // make private someday

  polyTask_step() : pinned(false), overhead(0.0) {}

  int64_t execute
  (const TaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> &, 
   polyTask_context &) const;
};

// PolyTask tuner class
template <int TASK_ID, int TASKTAGDIM, int INTERTASKDIM> class polyTask_tuner : 
  public CnC::step_tuner<>, public CnC::tag_tuner<>, public CnC::hashmap_tuner {
public:
  polyTask_tuner() {}
  template< class DC >
  void depends(const TaskTag<TASK_ID, TASKTAGDIM, INTERTASKDIM> &, 
               polyTask_context &, DC &) const;
};


// Context for a single polyTask 
class polyTask_context : 
public CnC::context< polyTask_context> {
public:
  polyTask_tuner <0, TASKTAGDIMMACRO, INTERTASKDIMMACRO> m_tuner;
  CnC::item_collection< TaskTag<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO>, 
                        void *, polyTask_tuner<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO> > M;
  CnC::step_collection< polyTask_step<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO>, 
                        polyTask_tuner<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO> 
                        > m_steps;
  CnC::tag_collection< TaskTag<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO> , 
                       polyTask_tuner<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO> 
                       > m_tags;

  CnC::item_collection< TaskTag<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO>, 
                        void *, polyTask_tuner<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO> >&
  getItemCollection(const TaskTag<0, TASKTAGDIMMACRO, INTERTASKDIMMACRO> &t) {
    return M;
  }  

  polyTask_context() : 
    m_tuner(), 
    M(*this, "M", m_tuner),  
    m_tags(*this), 
    m_steps(*this)
  {

    Threading::dumpThreadInfo(std::cerr, " polytask_init ");

    // prescription
    m_tags.prescribes( m_steps, *this );

    // producer/consumer
    m_steps.consumes( M );
    m_steps.produces( M );
  }

};


#endif
